#include "StdAfx.h"
#include "BPNetwork.h"
#include <cassert>
using namespace std;

#define LEARNRAT 0.2
#define MEMNORAT  0

void BPNetwork::Teach( const DoubleArray& inputData, const DoubleArray& teachData ) 
{
	_autoResize(inputData.size(),teachData.size());
	//cal 
	DoubleArray oData;
	oData.resize(teachData.size());
	Out(inputData,oData);

	//backward transfer error 
	DoubleArray deltaArray;
	for(size_t i=0;i<teachData.size();i++)
	{
		double delta = oData[i] * (1- oData[i]) * ( teachData[i] - oData[i] );
		BPNode& node = Node(mLevelNodesCount.size()-1,i);
		node.delta = delta;
	}
	int ilevel;
	//iterator all levels
	for(ilevel=mLevelNodesCount.size()-2;ilevel>0;ilevel--)
	{
		
		int nNodeCountNext =  mLevelNodesCount[ilevel+1];
		int nNodeCount =  mLevelNodesCount[ilevel];
		for(int i=0;i<nNodeCount;i++)
		{
			double dSum = 0;
			for(int j=0;j<nNodeCountNext;j++)
			{
				dSum += Node(ilevel+1,j).delta * Edge(NodeID(ilevel,i),NodeID(ilevel+1,j)).weight;
			}
			BPNode& node = Node(ilevel,i);
			node.delta = dSum * node.outputval * (1-node.outputval); 
		}
	}
	//update edge the weight
	for(ilevel = mLevelNodesCount.size()-2;ilevel>=0;ilevel--)
	{
		int nNodeCountNext = mLevelNodesCount[ilevel+1];
		int nNodeCount = mLevelNodesCount[ilevel];

		for(int i=0;i<nNodeCount;i++)//ilevel
		{
			for(int j=0;j<nNodeCountNext;j++) //ilevel+1
			{
				double dlchange = Node(ilevel+1,j).delta * Node(ilevel,i).outputval;
				BPEdge& edge = Edge(NodeID(ilevel,i),NodeID(ilevel+1,j));
				edge.weight += ( LEARNRAT* dlchange +  MEMNORAT * edge.dLastChange );
				edge.dLastChange = dlchange;				
			}
		}
	}
	//update the node threshold
	for(ilevel = mLevelNodesCount.size()-1;ilevel>0;ilevel--)
	{
		int nNodeCount = mLevelNodesCount[ilevel];
		for(int i=0;i<nNodeCount;i++)
		{
			BPNode& node = Node(ilevel,i);
			node.thres  -=  LEARNRAT * node.delta;
		}
	}

	


}

void BPNetwork::Teach( double dIn, double dOut )
{
	Teach(DoubleArray(&dIn,&dIn+1),DoubleArray(&dOut,&dOut+1));
}

void BPNetwork::Out( const DoubleArray& inputData, DoubleArray& outputData )
{
	//resize level node if needed
	_autoResize(inputData.size(),outputData.size());
	//set input data to level 0 nodes output
	for(size_t i=0;i<inputData.size();i++)
	{
		BPNode& node  = Node(0,i);
		node.outputval = inputData[i];
	}

	
	//get hidden nodes 
	int nLevelCount = mLevelNodesCount.size();
	for(int ilevel=1;ilevel<nLevelCount;++ilevel)
	{
		int nNodeCount = mLevelNodesCount[ilevel];
		for(int iIdx=0;iIdx<nNodeCount;iIdx++)
		{
			BPNode& node = Node(ilevel,iIdx);
			double nodeinput = _calNodeInput(ilevel,iIdx);
			node.input(nodeinput);
		}
	}
	//get data from the network
	for(size_t i=0;i<outputData.size();i++)
	{
		BPNode& node = Node(nLevelCount-1,i);
		outputData[i] = node.outputval;
	}
}

void BPNetwork::Out( double dIn,double& dOut )
{
	DoubleArray outArray(1); 
	Out(DoubleArray(&dIn,&dIn+1),outArray); 
	dOut = outArray.front();
}

double BPNetwork::_calNodeInput( int nLevel, int nIdx )
{
	assert(nLevel>0);
	if(nLevel<=0)
		return 0;

	double dret =0;
	NodeID tNodeID(nLevel,nIdx);
	int PreLevelCount = mLevelNodesCount[nLevel-1];
	for(int i=0;i<PreLevelCount;i++)
	{
		NodeID fNodeID(nLevel-1,i);
		
		BPEdge& edge = Edge(fNodeID,tNodeID);
		dret += edge.weight *  m_nodes[fNodeID].outputval;

	}
	return dret;
}

void BPNetwork::_autoResize( int nInput, int nOutput )
{
	if(mLevelNodesCount.size()>=2)
	{
		mLevelNodesCount.front() = max( nInput,mLevelNodesCount.front());
		mLevelNodesCount.back() = max( nOutput,mLevelNodesCount.back());
	}
}

void BPNetwork::Init( int nHiddenLevels, int NodeCount )
{
	mLevelNodesCount.clear();
	mLevelNodesCount.push_back(1);
	for(int i=0;i<nHiddenLevels;i++)
		mLevelNodesCount.push_back(NodeCount);
	mLevelNodesCount.push_back(1);
}

double Rand( double f,double t )
{
	return f + (t-f) * rand()/RAND_MAX;
}
